package demo.util;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TooManyListenersException;

@Slf4j
public class SerialPortUtils {

    /**
     * 获取端口名称列表
     *
     * @return
     */
    public static List<String> getPortNameList() {
        List<String> result = new ArrayList<>();

        Enumeration<CommPortIdentifier> serialPorts = CommPortIdentifier.getPortIdentifiers();

        while (!ObjectUtil.isEmpty(serialPorts) && serialPorts.hasMoreElements()) {
            result.add(serialPorts.nextElement().getName());
        }
        return result;
    }

    /**
     * 打开串口
     *
     * @param portName 串口名称
     * @param baudRate 波特率
     * @param dataBits 数据位
     * @param stopBits 停止位
     * @param parity   校验位
     * @return 串口
     */
    public static SerialPort open(String portName, Integer baudRate, Integer dataBits,
                                  Integer stopBits, Integer parity) {
        SerialPort result = null;
        try {
            // 通过串口名识别串口
            CommPortIdentifier identifier = CommPortIdentifier.getPortIdentifier(portName);

            // 打开串口(名称，超时时间)
            CommPort commPort = identifier.open(portName, 2000);

            // 判断是不是串口
            if (commPort instanceof SerialPort) {
                result = (SerialPort) commPort;

                // 设置串口参数
                result.setSerialPortParams(baudRate, dataBits, stopBits, parity);

                log.info("打开串口成功: " + portName);
            } else {
                log.info(portName + " 不是串口");
            }
        } catch (Exception e) {
            log.error("打开串口错误: " + portName + e.getMessage());
        }
        return result;
    }

    /**
     * 串口增加数据可用监听器
     *
     * @param serialPort
     * @param listener
     */
    public static void addListener(SerialPort serialPort, DataAvailableListener listener) {
        if (serialPort == null) {
            return;
        }
        try {
            // 给串口添加监听器
            serialPort.addEventListener(new SerialPortListener(listener));

            // 设置当有数据到达时唤醒监听接收线程
            serialPort.notifyOnDataAvailable(Boolean.TRUE);
            // 设置当通信中断时唤醒中断线程
            serialPort.notifyOnBreakInterrupt(Boolean.TRUE);

        } catch (TooManyListenersException e) {
            log.error("串口增加数据可用监听器错误: " + serialPort.getName() + e.getMessage());
        }
    }

    /**
     * 从串口读取数据
     *
     * @param serialPort
     * @return
     */
    public static byte[] read(SerialPort serialPort) {

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        byte[] result = {};
        if (serialPort == null) {
            return result;
        }
        InputStream inputStream = null;
        try {
            inputStream = serialPort.getInputStream();

            // 设置缓冲区大小为18个字节
            // todo 影响接收到的数据长度
            byte[] readBuffer = new byte[8];

            while (inputStream.available() > 0) {
                inputStream.read(readBuffer);
                result = ArrayUtil.addAll(result, readBuffer);
            }

        } catch (IOException e) {
            log.error("串口读取数据错误： " + serialPort.getName() + e.getMessage());
        } finally {
            IoUtil.close(inputStream);
        }
        return result;
    }

    /**
     * 往串口发送数据
     *
     * @param serialPort
     * @param data
     */
    public static void write(SerialPort serialPort, byte[] data) {
        if (serialPort == null) {
            return;
        }
        OutputStream outputStream = null;
        try {
            outputStream = serialPort.getOutputStream();
            outputStream.write(data);
            outputStream.flush();
        } catch (Exception e) {
            log.error("串口发送数据错误: " + serialPort.getName() + e.getMessage());
        } finally {
            IoUtil.close(outputStream);
        }
    }

    /**
     * 关闭串口
     *
     * @param serialPort
     */
    public static void close(SerialPort serialPort) {
        if (serialPort != null) {
            serialPort.close();
            log.warn("串口关闭: " + serialPort.getName());
        }
    }


}
